﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="BuildBunnySettingsModel.cs" company="Jim Liddell">
//   Copyright © Jim Liddell. All rights reserved.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------

namespace TeamBuildBunnyConfiguration.Models
{
    #region Usings

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Globalization;
    using System.Security.Principal;

    using Microsoft.TeamFoundation.Client;
    using Microsoft.TeamFoundation.Server;

    using TeamBuildBunny;

    using TeamBuildBunnyConfiguration.Properties;
    using TeamBuildBunnyConfiguration.ViewModels;

    #endregion

    /// <summary>
    /// Model class encapsulating settings functionality for a <see cref="BuildBunnySettingsViewModel"/>.
    /// </summary>
    public class BuildBunnySettingsModel : INotifyPropertyChanged
    {
        #region Constants and Fields

        /// <summary>
        /// Provides persistance of mapped settings.
        /// </summary>
        private readonly MappedSettings mappedSettings;

        /// <summary>
        /// The application settings.
        /// </summary>
        private readonly Settings settings;

        /// <summary>
        /// The id of the subscription to the build completion event.
        /// </summary>
        /// <remarks>A value of -1 represents no subscription.</remarks>
        private int buildCompletionEventSubscriptionId;

        /// <summary>
        /// The collection of <see cref="Nabaztag"/>.
        /// </summary>
        private NabaztagCollection bunnies;

        /// <summary>
        /// The id of the subscription to the checkin event.
        /// </summary>
        /// <remarks>A value of -1 represents no subscription.</remarks>
        private int checkInEventSubscriptionId;

        /// <summary>
        /// The URL of the Team Foundation Server.
        /// </summary>
        private string tfsUri;

        #endregion

        #region Constructors and Destructors

        /// <summary>
        /// Initializes a new instance of the <see cref="BuildBunnySettingsModel"/> class.
        /// </summary>
        /// <param name="fileName">
        /// The file Name for the mapped settings.
        /// </param>
        public BuildBunnySettingsModel(string fileName)
        {
            this.settings = Settings.Default;
            this.mappedSettings = new MappedSettings(fileName);

            this.Load();
        }

        #endregion

        #region Events

        /// <summary>
        /// Occurs when a property value changes.
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

        #endregion

        #region Properties

        /// <summary>
        /// Gets or sets the id of the subscription to the build completion event.
        /// </summary>
        public int BuildCompletionEventSubscriptionId
        {
            get
            {
                return this.buildCompletionEventSubscriptionId;
            }

            set
            {
                if (value != this.buildCompletionEventSubscriptionId)
                {
                    this.buildCompletionEventSubscriptionId = value;

                    this.OnPropertyChanged("BuildCompletionEventSubscriptionId");
                }
            }
        }

        /// <summary>
        /// Gets or sets the id of the subscription to the checkin event.
        /// </summary>
        public int CheckInEventSubscriptionId
        {
            get
            {
                return this.checkInEventSubscriptionId;
            }

            set
            {
                if (value != this.checkInEventSubscriptionId)
                {
                    this.checkInEventSubscriptionId = value;

                    this.OnPropertyChanged("CheckInEventSubscriptionId");
                }
            }
        }

        /// <summary>
        /// Gets the <see cref="Nabaztag"/>.
        /// </summary>
        public Nabaztag Nabaztag
        {
            get
            {
                if (this.bunnies.Count == 0)
                {
                    this.bunnies.Add(new Nabaztag());
                }

                return this.bunnies[0];
            }
        }

        /// <summary>
        /// Gets or sets the URI of the TFS server.
        /// </summary>
        public string TfsUri
        {
            get
            {
                return this.tfsUri;
            }

            set
            {
                if (value != this.tfsUri)
                {
                    this.tfsUri = value;

                    this.OnPropertyChanged("TfsUri");
                }
            }
        }

        #endregion

        #region Public Methods

        /// <summary>
        /// Returns the cultures available to assign to the <see cref="Nabaztag"/>.
        /// </summary>
        /// <returns>
        /// The cultures available to assign to the <see cref="Nabaztag"/>.
        /// </returns>
        public IEnumerable<CultureInfo> GetCultures()
        {
            return Nabaztag.Voices.GetCultures();
        }

        /// <summary>
        /// Gets the voices available to the <see cref="Nabaztag"/> for the specified culture.
        /// </summary>
        /// <param name="culture">
        /// The culture to filter the voices against.
        /// </param>
        /// <returns>
        /// The voices available to the <see cref="Nabaztag"/> for the specified culture.
        /// </returns>
        public IEnumerable<Nabaztag.Voice> GetVoices(CultureInfo culture)
        {
            return Nabaztag.Voices.GetVoices(culture);
        }

        /// <summary>
        /// Loads the settings.
        /// </summary>
        public void Load()
        {
            this.mappedSettings.Load();

            this.bunnies = this.mappedSettings.Bunnies;
            this.tfsUri = this.mappedSettings.TfsUri;

            this.buildCompletionEventSubscriptionId = this.settings.BuildCompletionEventSubscriptionId;
            this.checkInEventSubscriptionId = this.settings.CheckInEventSubscriptionId;
        }

        /// <summary>
        /// Saves both local and mapped settings.
        /// </summary>
        public void Save()
        {
            this.SaveSettings();
            this.SaveMappedSettings();
        }

        /// <summary>
        /// Saves the local settings for the application.
        /// </summary>
        private void SaveSettings()
        {
            this.settings.BuildCompletionEventSubscriptionId = this.buildCompletionEventSubscriptionId;
            this.settings.CheckInEventSubscriptionId = this.checkInEventSubscriptionId;

            this.settings.Save();
        }

        /// <summary>
        /// Saves the mapped settings.
        /// </summary>
        private void SaveMappedSettings()
        {
            this.mappedSettings.Bunnies = this.bunnies;
            this.mappedSettings.TfsUri = this.tfsUri;

            this.mappedSettings.Save();
        }

        /// <summary>
        /// The subscribe to build completion event.
        /// </summary>
        public void SubscribeToBuildCompletionEvent()
        {
            string userName = WindowsIdentity.GetCurrent().Name;
            string deliveryAddress = this.mappedSettings.BuildCompletionEndpointAddress.Replace("localhost", Environment.MachineName);

            this.BuildCompletionEventSubscriptionId = this.SubscribeToEvent(
                "BuildCompletionEvent", userName, deliveryAddress, "Team Build Bunny");

            this.SaveSettings();
        }

        /// <summary>
        /// The subscribe to check in event.
        /// </summary>
        public void SubscribeToCheckInEvent()
        {
            string userName = WindowsIdentity.GetCurrent().Name;
            string deliveryAddress = this.mappedSettings.CheckInEndpointAddress.Replace("localhost", Environment.MachineName);

            this.CheckInEventSubscriptionId =
                this.SubscribeToEvent("CheckinEvent", userName, deliveryAddress, "Team Build Bunny");

            this.SaveSettings();
        }

        /// <summary>
        /// Sends a test message to the <see cref="Nabaztag"/>.
        /// </summary>
        /// <param name="message">
        /// The test message.
        /// </param>
        public void TestNabaztag(string message)
        {
            this.Nabaztag.SendMessage(message, 0, 0);
        }

        /// <summary>
        /// Unsubscribes from the <see cref="BuildCompletionEvent"/>.
        /// </summary>
        public void UnsubscribeFromBuildCompletionEvent()
        {
            this.UnsubscribeFromEvent(this.BuildCompletionEventSubscriptionId);

            // reset subscription id
            this.BuildCompletionEventSubscriptionId = -1;

            this.SaveSettings();
        }

        /// <summary>
        /// Unsubscribes from the <see cref="CheckinEvent"/>.
        /// </summary>
        public void UnsubscribeFromCheckInEvent()
        {
            this.UnsubscribeFromEvent(this.CheckInEventSubscriptionId);

            // reset subscription id
            this.CheckInEventSubscriptionId = -1;

            this.SaveSettings();
        }

        /// <summary>
        /// The unsubscribe from event.
        /// </summary>
        /// <param name="subscriptionId">
        /// The subscription id.
        /// </param>
        public void UnsubscribeFromEvent(int subscriptionId)
        {
            TeamFoundationServer tfs = TeamFoundationServerFactory.GetServer(this.tfsUri);
            IEventService eventService = tfs.GetService(typeof(IEventService)) as IEventService;
            eventService.UnsubscribeEvent(subscriptionId);
        }

        #endregion

        #region Methods

        /// <summary>
        /// Raises the <see cref="INotifyPropertyChanged.PropertyChanged"/> event.
        /// </summary>
        /// <param name="propertyName">
        /// The name of the property that changed.
        /// </param>
        private void OnPropertyChanged(string propertyName)
        {
            if (this.PropertyChanged != null)
            {
                this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        /// <summary>
        /// The subscribe to event.
        /// </summary>
        /// <param name="eventType">
        /// The event type.
        /// </param>
        /// <param name="userName">
        /// The user name.
        /// </param>
        /// <param name="deliveryAddress">
        /// The delivery address.
        /// </param>
        /// <param name="tag">
        /// A tag for the event.</param>
        /// <returns>
        /// The subscribe to event.
        /// </returns>
        private int SubscribeToEvent(string eventType, string userName, string deliveryAddress, string tag)
        {
            TeamFoundationServer tfs = TeamFoundationServerFactory.GetServer(this.tfsUri);
            IEventService eventService = tfs.GetService(typeof(IEventService)) as IEventService;

            DeliveryPreference delivery = new DeliveryPreference
                {
                    Type = DeliveryType.Soap, Schedule = DeliverySchedule.Immediate, Address = deliveryAddress 
                };

            return eventService.SubscribeEvent(userName, eventType, string.Empty, delivery, tag);
        }

        #endregion
    }
}